1 module hip.jni.android.android_native_app_glue; 2 version(Android): 3 /* 4 * Copyright (C) 2010 The Android Open Source Project 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 */ 19 20 import core.runtime : rt_init, rt_term; 21 import core.stdc.errno : errno; 22 import core.stdc.stdarg : va_list, va_start; 23 import core.stdc.stdlib : free, malloc; 24 import core.stdc.string : memcpy, memset, strerror; 25 import core.sys.posix.pthread, core.sys.posix.unistd : close, pipe, read, write; 26 27 import hip.jni.android.input, hip.jni.android.native_window, hip.jni.android.rect : ARect; 28 import hip.jni.android.configuration, hip.jni.android.log, hip.jni.android.looper, hip.jni.android.native_activity; 29 30 version (Android) @system: 31 32 /** 33 * The native activity interface provided by <android/native_activity.h> 34 * is based on a set of application-provided callbacks that will be called 35 * by the Activity's main thread when certain events occur. 36 * 37 * This means that each one of this callbacks _should_ _not_ block, or they 38 * risk having the system force-close the application. This programming 39 * model is direct, lightweight, but constraining. 40 * 41 * The 'android_native_app_glue' static library is used to provide a different 42 * execution model where the application can implement its own main event 43 * loop in a different thread instead. Here's how it works: 44 * 45 * 1/ The application must provide a function named "android_main()" that 46 * will be called when the activity is created, in a new thread that is 47 * distinct from the activity's main thread. 48 * 49 * 2/ android_main() receives a pointer to a valid "android_app" structure 50 * that contains references to other important objects, e.g. the 51 * ANativeActivity obejct instance the application is running in. 52 * 53 * 3/ the "android_app" object holds an ALooper instance that already 54 * listens to two important things: 55 * 56 * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX 57 * declarations below. 58 * 59 * - input events coming from the AInputQueue attached to the activity. 60 * 61 * Each of these correspond to an ALooper identifier returned by 62 * ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT, 63 * respectively. 64 * 65 * Your application can use the same ALooper to listen to additional 66 * file-descriptors. They can either be callback based, or with return 67 * identifiers starting with LOOPER_ID_USER. 68 * 69 * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event, 70 * the returned data will point to an android_poll_source structure. You 71 * can call the process() function on it, and fill in android_app.onAppCmd 72 * and android_app.onInputEvent to be called for your own processing 73 * of the event. 74 * 75 * Alternatively, you can call the low-level functions to read and process 76 * the data directly... look at the process_cmd() and process_input() 77 * implementations in the glue to see how to do this. 78 * 79 * See the sample named "native-activity" that comes with the NDK with a 80 * full usage example. Also look at the JavaDoc of NativeActivity. 81 */ 82 83 /** 84 * Data associated with an ALooper fd that will be returned as the "outData" 85 * when that source has data ready. 86 */ 87 struct android_poll_source { 88 // The identifier of this source. May be LOOPER_ID_MAIN or 89 // LOOPER_ID_INPUT. 90 int id; 91 92 // The android_app this ident is associated with. 93 android_app* app; 94 95 // Function to call to perform the standard processing of data from 96 // this source. 97 void function(android_app*, android_poll_source*) process; 98 } 99 100 /** 101 * This is the interface for the standard glue code of a threaded 102 * application. In this model, the application's code is running 103 * in its own thread separate from the main thread of the process. 104 * It is not required that this thread be associated with the Java 105 * VM, although it will need to be in order to make JNI calls any 106 * Java objects. 107 */ 108 struct android_app { 109 // The application can place a pointer to its own state object 110 // here if it likes. 111 void* userData; 112 113 // Fill this in with the function to process main app commands (APP_CMD_*) 114 void function(android_app*, int) onAppCmd; 115 116 // Fill this in with the function to process input events. At this point 117 // the event has already been pre-dispatched, and it will be finished upon 118 // return. Return 1 if you have handled the event, 0 for any default 119 // dispatching. 120 int function(android_app*, AInputEvent*) onInputEvent; 121 122 // The ANativeActivity object instance that this app is running in. 123 ANativeActivity* activity; 124 125 // The current configuration the app is running in. 126 AConfiguration* config; 127 128 // This is the last instance's saved state, as provided at creation time. 129 // It is null if there was no state. You can use this as you need; the 130 // memory will remain around until you call android_app_exec_cmd() for 131 // APP_CMD_RESUME, at which point it will be freed and savedState set to null. 132 // These variables should only be changed when processing a APP_CMD_SAVE_STATE, 133 // at which point they will be initialized to null and you can malloc your 134 // state and place the information here. In that case the memory will be 135 // freed for you later. 136 void* savedState; 137 size_t savedStateSize; 138 139 // The ALooper associated with the app's thread. 140 ALooper* looper; 141 142 // When non-null, this is the input queue from which the app will 143 // receive user input events. 144 AInputQueue* inputQueue; 145 146 // When non-null, this is the window surface that the app can draw in. 147 ANativeWindow* window; 148 149 // Current content rectangle of the window; this is the area where the 150 // window's content should be placed to be seen by the user. 151 ARect contentRect; 152 153 // Current state of the app's activity. May be either APP_CMD_START, 154 // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. 155 int activityState; 156 157 // This is non-zero when the application's NativeActivity is being 158 // destroyed and waiting for the app thread to complete. 159 int destroyRequested; 160 161 // ------------------------------------------------- 162 // Below are "private" implementation of the glue code. 163 164 pthread_mutex_t mutex; 165 pthread_cond_t cond; 166 167 int msgread; 168 int msgwrite; 169 170 pthread_t thread; 171 172 android_poll_source cmdPollSource; 173 android_poll_source inputPollSource; 174 175 int running; 176 int stateSaved; 177 int destroyed; 178 int redrawNeeded; 179 AInputQueue* pendingInputQueue; 180 ANativeWindow* pendingWindow; 181 ARect pendingContentRect; 182 } 183 184 enum { 185 /** 186 * Looper data ID of commands coming from the app's main thread, which 187 * is returned as an identifier from ALooper_pollOnce(). The data for this 188 * identifier is a pointer to an android_poll_source structure. 189 * These can be retrieved and processed with android_app_read_cmd() 190 * and android_app_exec_cmd(). 191 */ 192 LOOPER_ID_MAIN = 1, 193 194 /** 195 * Looper data ID of events coming from the AInputQueue of the 196 * application's window, which is returned as an identifier from 197 * ALooper_pollOnce(). The data for this identifier is a pointer to an 198 * android_poll_source structure. These can be read via the inputQueue 199 * object of android_app. 200 */ 201 LOOPER_ID_INPUT = 2, 202 203 /** 204 * Start of user-defined ALooper identifiers. 205 */ 206 LOOPER_ID_USER = 3, 207 } 208 209 enum { 210 /** 211 * Command from main thread: the AInputQueue has changed. Upon processing 212 * this command, android_app.inputQueue will be updated to the new queue 213 * (or null). 214 */ 215 APP_CMD_INPUT_CHANGED, 216 217 /** 218 * Command from main thread: a new ANativeWindow is ready for use. Upon 219 * receiving this command, android_app.window will contain the new window 220 * surface. 221 */ 222 APP_CMD_INIT_WINDOW, 223 224 /** 225 * Command from main thread: the existing ANativeWindow needs to be 226 * terminated. Upon receiving this command, android_app.window still 227 * contains the existing window; after calling android_app_exec_cmd 228 * it will be set to null. 229 */ 230 APP_CMD_TERM_WINDOW, 231 232 /** 233 * Command from main thread: the current ANativeWindow has been resized. 234 * Please redraw with its new size. 235 */ 236 APP_CMD_WINDOW_RESIZED, 237 238 /** 239 * Command from main thread: the system needs that the current ANativeWindow 240 * be redrawn. You should redraw the window before handing this to 241 * android_app_exec_cmd() in order to avoid transient drawing glitches. 242 */ 243 APP_CMD_WINDOW_REDRAW_NEEDED, 244 245 /** 246 * Command from main thread: the content area of the window has changed, 247 * such as from the soft input window being shown or hidden. You can 248 * find the new content rect in android_app::contentRect. 249 */ 250 APP_CMD_CONTENT_RECT_CHANGED, 251 252 /** 253 * Command from main thread: the app's activity window has gained 254 * input focus. 255 */ 256 APP_CMD_GAINED_FOCUS, 257 258 /** 259 * Command from main thread: the app's activity window has lost 260 * input focus. 261 */ 262 APP_CMD_LOST_FOCUS, 263 264 /** 265 * Command from main thread: the current device configuration has changed. 266 */ 267 APP_CMD_CONFIG_CHANGED, 268 269 /** 270 * Command from main thread: the system is running low on memory. 271 * Try to reduce your memory use. 272 */ 273 APP_CMD_LOW_MEMORY, 274 275 /** 276 * Command from main thread: the app's activity has been started. 277 */ 278 APP_CMD_START, 279 280 /** 281 * Command from main thread: the app's activity has been resumed. 282 */ 283 APP_CMD_RESUME, 284 285 /** 286 * Command from main thread: the app should generate a new saved state 287 * for itself, to restore from later if needed. If you have saved state, 288 * allocate it with malloc and place it in android_app.savedState with 289 * the size in android_app.savedStateSize. The will be freed for you 290 * later. 291 */ 292 APP_CMD_SAVE_STATE, 293 294 /** 295 * Command from main thread: the app's activity has been paused. 296 */ 297 APP_CMD_PAUSE, 298 299 /** 300 * Command from main thread: the app's activity has been stopped. 301 */ 302 APP_CMD_STOP, 303 304 /** 305 * Command from main thread: the app's activity is being destroyed, 306 * and waiting for the app thread to clean up and exit before proceeding. 307 */ 308 APP_CMD_DESTROY, 309 } 310 311 /** 312 * This is the function that application code must implement, representing 313 * the main entry to the app. 314 */ 315 extern(C) void android_main(android_app* app); 316 317 private: 318 int LOGI(const(char)* warning) { return __android_log_print(android_LogPriority.ANDROID_LOG_INFO, "threaded_app", warning); } 319 int LOGE(const(char)* fmt, ...) { 320 va_list arg_list; 321 va_start(arg_list, fmt); 322 return __android_log_print(android_LogPriority.ANDROID_LOG_ERROR, "threaded_app", fmt, arg_list); 323 } 324 325 /* For debug builds, always enable the debug traces in this library */ 326 int LOGV(const(char)* fmt, ...) { 327 debug { 328 va_list arg_list; 329 va_start(arg_list, fmt); 330 return __android_log_print(android_LogPriority.ANDROID_LOG_VERBOSE, "threaded_app", fmt, arg_list); 331 } else 332 return 0; 333 } 334 335 void free_saved_state(android_app* android_app) { 336 pthread_mutex_lock(&android_app.mutex); 337 if (android_app.savedState != null) { 338 free(android_app.savedState); 339 android_app.savedState = null; 340 android_app.savedStateSize = 0; 341 } 342 pthread_mutex_unlock(&android_app.mutex); 343 } 344 345 void print_cur_config(android_app* android_app) { 346 char[2] lang, country; 347 AConfiguration_getLanguage(android_app.config, lang.ptr); 348 AConfiguration_getCountry(android_app.config, country.ptr); 349 350 LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " 351 ~ "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " 352 ~ "modetype=%d modenight=%d", 353 AConfiguration_getMcc(android_app.config), 354 AConfiguration_getMnc(android_app.config), 355 lang[0], lang[1], country[0], country[1], 356 AConfiguration_getOrientation(android_app.config), 357 AConfiguration_getTouchscreen(android_app.config), 358 AConfiguration_getDensity(android_app.config), 359 AConfiguration_getKeyboard(android_app.config), 360 AConfiguration_getNavigation(android_app.config), 361 AConfiguration_getKeysHidden(android_app.config), 362 AConfiguration_getNavHidden(android_app.config), 363 AConfiguration_getSdkVersion(android_app.config), 364 AConfiguration_getScreenSize(android_app.config), 365 AConfiguration_getScreenLong(android_app.config), 366 AConfiguration_getUiModeType(android_app.config), 367 AConfiguration_getUiModeNight(android_app.config)); 368 } 369 370 public: 371 /** 372 * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next 373 * app command message. 374 */ 375 byte android_app_read_cmd(android_app* android_app) { 376 byte cmd; 377 if (read(android_app.msgread, &cmd, cmd.sizeof) == cmd.sizeof) { 378 switch (cmd) { 379 case APP_CMD_SAVE_STATE: 380 free_saved_state(android_app); 381 break; 382 default: 383 break; 384 } 385 return cmd; 386 } else { 387 LOGE("No data on command pipe!"); 388 } 389 return -1; 390 } 391 392 /** 393 * Call with the command returned by android_app_read_cmd() to do the 394 * initial pre-processing of the given command. You can perform your own 395 * actions for the command after calling this function. 396 */ 397 void android_app_pre_exec_cmd(android_app* android_app, byte cmd) { 398 switch (cmd) { 399 case APP_CMD_INPUT_CHANGED: 400 LOGV("APP_CMD_INPUT_CHANGED\n"); 401 pthread_mutex_lock(&android_app.mutex); 402 if (android_app.inputQueue != null) { 403 AInputQueue_detachLooper(android_app.inputQueue); 404 } 405 android_app.inputQueue = android_app.pendingInputQueue; 406 if (android_app.inputQueue != null) { 407 LOGV("Attaching input queue to looper"); 408 AInputQueue_attachLooper(android_app.inputQueue, 409 android_app.looper, LOOPER_ID_INPUT, null, 410 &android_app.inputPollSource); 411 } 412 pthread_cond_broadcast(&android_app.cond); 413 pthread_mutex_unlock(&android_app.mutex); 414 break; 415 416 case APP_CMD_INIT_WINDOW: 417 LOGV("APP_CMD_INIT_WINDOW\n"); 418 pthread_mutex_lock(&android_app.mutex); 419 android_app.window = android_app.pendingWindow; 420 pthread_cond_broadcast(&android_app.cond); 421 pthread_mutex_unlock(&android_app.mutex); 422 break; 423 424 case APP_CMD_TERM_WINDOW: 425 LOGV("APP_CMD_TERM_WINDOW\n"); 426 pthread_cond_broadcast(&android_app.cond); 427 break; 428 429 case APP_CMD_RESUME: 430 case APP_CMD_START: 431 case APP_CMD_PAUSE: 432 case APP_CMD_STOP: 433 LOGV("activityState=%d\n", cmd); 434 pthread_mutex_lock(&android_app.mutex); 435 android_app.activityState = cmd; 436 pthread_cond_broadcast(&android_app.cond); 437 pthread_mutex_unlock(&android_app.mutex); 438 break; 439 440 case APP_CMD_CONFIG_CHANGED: 441 LOGV("APP_CMD_CONFIG_CHANGED\n"); 442 AConfiguration_fromAssetManager(android_app.config, 443 android_app.activity.assetManager); 444 print_cur_config(android_app); 445 break; 446 447 case APP_CMD_DESTROY: 448 LOGV("APP_CMD_DESTROY\n"); 449 android_app.destroyRequested = 1; 450 break; 451 default: 452 break; 453 } 454 } 455 456 /** 457 * Call with the command returned by android_app_read_cmd() to do the 458 * final post-processing of the given command. You must have done your own 459 * actions for the command before calling this function. 460 */ 461 void android_app_post_exec_cmd(android_app* android_app, byte cmd) { 462 switch (cmd) { 463 case APP_CMD_TERM_WINDOW: 464 LOGV("APP_CMD_TERM_WINDOW\n"); 465 pthread_mutex_lock(&android_app.mutex); 466 android_app.window = null; 467 pthread_cond_broadcast(&android_app.cond); 468 pthread_mutex_unlock(&android_app.mutex); 469 break; 470 471 case APP_CMD_SAVE_STATE: 472 LOGV("APP_CMD_SAVE_STATE\n"); 473 pthread_mutex_lock(&android_app.mutex); 474 android_app.stateSaved = 1; 475 pthread_cond_broadcast(&android_app.cond); 476 pthread_mutex_unlock(&android_app.mutex); 477 break; 478 479 case APP_CMD_RESUME: 480 free_saved_state(android_app); 481 break; 482 default: 483 break; 484 } 485 } 486 487 /** 488 * Dummy function you can call to ensure glue code isn't stripped. 489 */ 490 void app_dummy() { 491 492 } 493 494 private: 495 void android_app_destroy(android_app* android_app) { 496 LOGV("android_app_destroy!"); 497 free_saved_state(android_app); 498 pthread_mutex_lock(&android_app.mutex); 499 if (android_app.inputQueue != null) { 500 AInputQueue_detachLooper(android_app.inputQueue); 501 } 502 AConfiguration_delete(android_app.config); 503 android_app.destroyed = 1; 504 pthread_cond_broadcast(&android_app.cond); 505 pthread_mutex_unlock(&android_app.mutex); 506 // Can't touch android_app object after this. 507 } 508 509 void process_input(android_app* app, android_poll_source* source) { 510 AInputEvent* event = null; 511 while (AInputQueue_getEvent(app.inputQueue, &event) >= 0) { 512 LOGV("New input event: type=%d\n", AInputEvent_getType(event)); 513 if (AInputQueue_preDispatchEvent(app.inputQueue, event)) { 514 continue; 515 } 516 int handled = 0; 517 if (app.onInputEvent != null) handled = app.onInputEvent(app, event); 518 AInputQueue_finishEvent(app.inputQueue, event, handled); 519 } 520 } 521 522 void process_cmd(android_app* app, android_poll_source* source) { 523 byte cmd = android_app_read_cmd(app); 524 android_app_pre_exec_cmd(app, cmd); 525 if (app.onAppCmd != null) app.onAppCmd(app, cmd); 526 android_app_post_exec_cmd(app, cmd); 527 } 528 529 extern(C) void* android_app_entry(void* param) { 530 android_app* android_app = cast(android_app*)param; 531 532 android_app.config = AConfiguration_new(); 533 AConfiguration_fromAssetManager(android_app.config, android_app.activity.assetManager); 534 535 print_cur_config(android_app); 536 537 android_app.cmdPollSource.id = LOOPER_ID_MAIN; 538 android_app.cmdPollSource.app = android_app; 539 android_app.cmdPollSource.process = &process_cmd; 540 android_app.inputPollSource.id = LOOPER_ID_INPUT; 541 android_app.inputPollSource.app = android_app; 542 android_app.inputPollSource.process = &process_input; 543 544 ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); 545 ALooper_addFd(looper, android_app.msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, null, 546 &android_app.cmdPollSource); 547 android_app.looper = looper; 548 549 pthread_mutex_lock(&android_app.mutex); 550 android_app.running = 1; 551 pthread_cond_broadcast(&android_app.cond); 552 pthread_mutex_unlock(&android_app.mutex); 553 554 rt_init(); 555 android_main(android_app); 556 rt_term(); 557 558 android_app_destroy(android_app); 559 return null; 560 } 561 562 // -------------------------------------------------------------------- 563 // Native activity interaction (called from main thread) 564 // -------------------------------------------------------------------- 565 566 android_app* android_app_create(ANativeActivity* activity, 567 void* savedState, size_t savedStateSize) { 568 android_app* andro_app = cast(android_app*)malloc(android_app.sizeof); 569 memset(andro_app, 0, android_app.sizeof); 570 andro_app.activity = activity; 571 572 pthread_mutex_init(&andro_app.mutex, null); 573 pthread_cond_init(&andro_app.cond, null); 574 575 if (savedState != null) { 576 andro_app.savedState = malloc(savedStateSize); 577 andro_app.savedStateSize = savedStateSize; 578 memcpy(andro_app.savedState, savedState, savedStateSize); 579 } 580 581 int[2] msgpipe; 582 if (pipe(msgpipe)) { 583 LOGE("could not create pipe: %s", strerror(errno)); 584 return null; 585 } 586 andro_app.msgread = msgpipe[0]; 587 andro_app.msgwrite = msgpipe[1]; 588 589 pthread_attr_t attr; 590 pthread_attr_init(&attr); 591 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 592 pthread_create(&andro_app.thread, &attr, &android_app_entry, andro_app); 593 594 // Wait for thread to start. 595 pthread_mutex_lock(&andro_app.mutex); 596 while (!andro_app.running) { 597 pthread_cond_wait(&andro_app.cond, &andro_app.mutex); 598 } 599 pthread_mutex_unlock(&andro_app.mutex); 600 601 return andro_app; 602 } 603 604 void android_app_write_cmd(android_app* android_app, byte cmd) { 605 if (write(android_app.msgwrite, &cmd, cmd.sizeof) != cmd.sizeof) { 606 LOGE("Failure writing android_app cmd: %s\n", strerror(errno)); 607 } 608 } 609 610 void android_app_set_input(android_app* android_app, AInputQueue* inputQueue) { 611 pthread_mutex_lock(&android_app.mutex); 612 android_app.pendingInputQueue = inputQueue; 613 android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); 614 while (android_app.inputQueue != android_app.pendingInputQueue) { 615 pthread_cond_wait(&android_app.cond, &android_app.mutex); 616 } 617 pthread_mutex_unlock(&android_app.mutex); 618 } 619 620 void android_app_set_window(android_app* android_app, ANativeWindow* window) { 621 pthread_mutex_lock(&android_app.mutex); 622 if (android_app.pendingWindow != null) { 623 android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); 624 } 625 android_app.pendingWindow = window; 626 if (window != null) { 627 android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW); 628 } 629 while (android_app.window != android_app.pendingWindow) { 630 pthread_cond_wait(&android_app.cond, &android_app.mutex); 631 } 632 pthread_mutex_unlock(&android_app.mutex); 633 } 634 635 void android_app_set_activity_state(android_app* android_app, byte cmd) { 636 pthread_mutex_lock(&android_app.mutex); 637 android_app_write_cmd(android_app, cmd); 638 while (android_app.activityState != cmd) { 639 pthread_cond_wait(&android_app.cond, &android_app.mutex); 640 } 641 pthread_mutex_unlock(&android_app.mutex); 642 } 643 644 void android_app_free(android_app* android_app) { 645 pthread_mutex_lock(&android_app.mutex); 646 android_app_write_cmd(android_app, APP_CMD_DESTROY); 647 while (!android_app.destroyed) { 648 pthread_cond_wait(&android_app.cond, &android_app.mutex); 649 } 650 pthread_mutex_unlock(&android_app.mutex); 651 652 close(android_app.msgread); 653 close(android_app.msgwrite); 654 pthread_cond_destroy(&android_app.cond); 655 pthread_mutex_destroy(&android_app.mutex); 656 free(android_app); 657 } 658 659 extern(C) void onDestroy(ANativeActivity* activity) { 660 LOGV("Destroy: %p\n", activity); 661 android_app_free(cast(android_app*)activity.instance); 662 } 663 664 extern(C) void onStart(ANativeActivity* activity) { 665 LOGV("Start: %p\n", activity); 666 android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_START); 667 } 668 669 extern(C) void onResume(ANativeActivity* activity) { 670 LOGV("Resume: %p\n", activity); 671 android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_RESUME); 672 } 673 674 extern(C) void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { 675 android_app* android_app = cast(android_app*)activity.instance; 676 void* savedState = null; 677 678 LOGV("SaveInstanceState: %p\n", activity); 679 pthread_mutex_lock(&android_app.mutex); 680 android_app.stateSaved = 0; 681 android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); 682 while (!android_app.stateSaved) { 683 pthread_cond_wait(&android_app.cond, &android_app.mutex); 684 } 685 686 if (android_app.savedState != null) { 687 savedState = android_app.savedState; 688 *outLen = android_app.savedStateSize; 689 android_app.savedState = null; 690 android_app.savedStateSize = 0; 691 } 692 693 pthread_mutex_unlock(&android_app.mutex); 694 695 return savedState; 696 } 697 698 extern(C) void onPause(ANativeActivity* activity) { 699 LOGV("Pause: %p\n", activity); 700 android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_PAUSE); 701 } 702 703 extern(C) void onStop(ANativeActivity* activity) { 704 LOGV("Stop: %p\n", activity); 705 android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_STOP); 706 } 707 708 extern(C) void onConfigurationChanged(ANativeActivity* activity) { 709 android_app* android_app = cast(android_app*)activity.instance; 710 LOGV("ConfigurationChanged: %p\n", activity); 711 android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED); 712 } 713 714 extern(C) void onLowMemory(ANativeActivity* activity) { 715 android_app* android_app = cast(android_app*)activity.instance; 716 LOGV("LowMemory: %p\n", activity); 717 android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY); 718 } 719 720 extern(C) void onWindowFocusChanged(ANativeActivity* activity, int focused) { 721 LOGV("WindowFocusChanged: %p -- %d\n", activity, focused); 722 android_app_write_cmd(cast(android_app*)activity.instance, 723 focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); 724 } 725 726 extern(C) void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { 727 LOGV("NativeWindowCreated: %p -- %p\n", activity, window); 728 android_app_set_window(cast(android_app*)activity.instance, window); 729 } 730 731 extern(C) void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { 732 LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window); 733 android_app_set_window(cast(android_app*)activity.instance, null); 734 } 735 736 extern(C) void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { 737 LOGV("InputQueueCreated: %p -- %p\n", activity, queue); 738 android_app_set_input(cast(android_app*)activity.instance, queue); 739 } 740 741 extern(C) void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { 742 LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue); 743 android_app_set_input(cast(android_app*)activity.instance, null); 744 } 745 746 public: 747 extern(C) void ANativeActivity_onCreate(ANativeActivity* activity, 748 void* savedState, size_t savedStateSize) { 749 LOGV("Creating: %p\n", activity); 750 activity.callbacks.onDestroy = &onDestroy; 751 activity.callbacks.onStart = &onStart; 752 activity.callbacks.onResume = &onResume; 753 activity.callbacks.onSaveInstanceState = &onSaveInstanceState; 754 activity.callbacks.onPause = &onPause; 755 activity.callbacks.onStop = &onStop; 756 activity.callbacks.onConfigurationChanged = &onConfigurationChanged; 757 activity.callbacks.onLowMemory = &onLowMemory; 758 activity.callbacks.onWindowFocusChanged = &onWindowFocusChanged; 759 activity.callbacks.onNativeWindowCreated = &onNativeWindowCreated; 760 activity.callbacks.onNativeWindowDestroyed = &onNativeWindowDestroyed; 761 activity.callbacks.onInputQueueCreated = &onInputQueueCreated; 762 activity.callbacks.onInputQueueDestroyed = &onInputQueueDestroyed; 763 764 activity.instance = android_app_create(activity, savedState, savedStateSize); 765 }